import os
import shutil
import logging
import requests
import oss2
from pathlib import Path
from typing import Optional

from config import settings

logger = logging.getLogger("mobox-server.file-service")

class FileService:
    """文件服务类，用于管理用户文件和课程文件"""
    
    def __init__(self):
        """初始化文件服务"""
        self.course_base_path = settings.COURSE_BASE_PATH
        
        # 阿里云OSS配置
        self.oss_endpoint = settings.OSS_ENDPOINT
        self.oss_bucket_name = settings.OSS_BUCKET_NAME
        self.oss_region = settings.OSS_REGION
        self.oss_access_key_id = settings.OSS_ACCESS_KEY_ID
        self.oss_access_key_secret = settings.OSS_ACCESS_KEY_SECRET
        
        # 初始化OSS客户端
        self.auth = oss2.AuthV4(self.oss_access_key_id, self.oss_access_key_secret)
        self.bucket = oss2.Bucket(self.auth, self.oss_endpoint, self.oss_bucket_name, region=self.oss_region)
    
    def ensure_experiment_directory(self, user_id: str, course_name: str, experiment_id: str) -> str:
        """
        确保实验目录存在，如果不存在则创建和下载课程资源
        
        Args:
            user_id: 用户ID
            course_name: 课程名称
            experiment_id: 实验ID
            
        Returns:
            str: 实验目录完整路径
        """
        # 构建用户目录路径: /course/stu1
        user_dir = os.path.join(self.course_base_path, user_id)
        
        # 检查用户目录是否存在
        if not os.path.exists(user_dir):
            logger.info(f"创建用户目录: {user_dir}")
            os.makedirs(user_dir, exist_ok=True)
        
        # 构建课程目录路径: /course/stu1/segment_magic
        course_dir = os.path.join(user_dir, course_name)
        
        # 检查课程目录是否存在
        if not os.path.exists(course_dir):
            logger.info(f"课程目录不存在，准备下载课程: {course_dir}")
            os.makedirs(course_dir, exist_ok=True)
            
            # 下载整个课程
            self._download_course_from_oss(course_name, course_dir)
        
        # 构建实验目录路径: /course/stu1/segment_magic/exp1
        experiment_dir = os.path.join(course_dir, experiment_id)
        
        # 检查实验目录是否存在
        if not os.path.exists(experiment_dir):
            logger.info(f"创建实验目录: {experiment_dir}")
            os.makedirs(experiment_dir, exist_ok=True)
        
        return experiment_dir
    
    def _download_course_from_oss(self, course_name: str, target_dir: str) -> None:
        """
        从阿里云OSS下载整个课程资源
        
        Args:
            course_name: 课程名称
            target_dir: 目标目录
        """
        try:
            logger.info(f"从OSS下载课程 {course_name} 到 {target_dir}")
            
            # 列出课程目录下的所有文件
            course_prefix = f"{course_name}/"
            logger.info(f"查询OSS前缀: {course_prefix}")
            object_list = []
            
            # 使用list_objects分页列出所有对象
            next_marker = None
            while True:
                if next_marker:
                    result = self.bucket.list_objects(prefix=course_prefix, marker=next_marker)
                else:
                    result = self.bucket.list_objects(prefix=course_prefix)
                
                logger.info(f"OSS查询返回对象数量: {len(result.object_list)}")
                for obj in result.object_list:
                    logger.info(f"发现OSS对象: {obj.key}")
                    if not obj.key.endswith('/'):  # 排除目录对象
                        object_list.append(obj.key)
                
                # 如果结果被截断，继续获取下一页
                if result.is_truncated:
                    next_marker = result.next_marker
                else:
                    break
            
            logger.info(f"找到文件总数: {len(object_list)}")
            
            # 下载所有文件
            for obj_key in object_list:
                # 计算本地文件路径
                relative_path = obj_key.replace(course_prefix, '', 1)
                local_file_path = os.path.join(target_dir, relative_path)
                
                # 确保目录存在
                os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
                
                # 下载文件
                logger.info(f"下载文件: {obj_key} 到 {local_file_path}")
                self.bucket.get_object_to_file(obj_key, local_file_path)
            
            logger.info(f"课程 {course_name} 下载完成")
            
        except Exception as e:
            logger.error(f"从OSS下载课程时出错: {str(e)}", exc_info=True)
            # 这里修改为打印更多信息但不抛出异常，让API继续运行
            logger.error(f"OSS错误详情: {e.__class__.__name__}: {str(e)}")
            # raise ValueError(f"无法下载课程资源: {str(e)}")
    
    def get_experiment_file_path(self, user_id: str, course_type: str, experiment_id: str) -> Optional[str]:
        """
        获取实验文件的路径
        
        Args:
            user_id: 用户ID
            course_type: 课程类型
            experiment_id: 实验ID
            
        Returns:
            Optional[str]: 实验文件路径，如果不存在则返回None
        """
        course_dir = self.ensure_experiment_directory(user_id, course_type, experiment_id)
        experiment_file = os.path.join(course_dir, "experiments", f"{experiment_id}.json")
        
        if not os.path.exists(experiment_file):
            logger.warning(f"实验文件不存在: {experiment_file}")
            return None
            
        return experiment_file 